#pragma once

/*--------------------------------------------------------------------------*/

struct ERenderPhaseFlag
{
	enum Flag
	{

	};
};

/*--------------------------------------------------------------------------*/

struct EMatrixDirtyFlag
{
	enum Flag
	{
	    ModelMatrixChanged		= 1 << 0,
	    ViewMatrixChanged		= 1 << 1,
	    ProjectionMatrixChanged = 1 << 2,
	    MVPChanged				= 1 << 3
	};
};

/*--------------------------------------------------------------------------*/

struct RenderPhase:
	public ICriticalSection
{
	SceneInstance*				m_sceneInstance;
	SceneCamera*				m_sceneCamera;
	ERenderPhaseMethod::Method	m_method;
	uint						m_renderphaseFlags;
	uint						m_postprocessFlags;
	int							m_phasePriority;
	std::string					m_name;

	/*--------------------------------------------------------------------------*/
	std::vector<RenderTask*>*	m_rendertasks;
	GLFramebuffer**				m_framebuffer;
	uint						m_numFramebuffer;
	/*--------------------------------------------------------------------------*/
	std::vector<IPostprocess*>	m_postprocesses;
	GLTexture*					m_inPPFX;
	GLTexture*					m_outPPFX;
	GLFramebuffer*				m_lastFBO;
	/*--------------------------------------------------------------------------*/

	RenderPhase() :
		ICriticalSection(1),
		m_sceneInstance(NULL),
		m_sceneCamera(NULL),
		m_method(ERenderPhaseMethod::DeferredShading),
		m_renderphaseFlags(0),
		m_postprocessFlags(0),
		m_phasePriority(0),
		m_framebuffer(NULL),
		m_numFramebuffer(0),
		m_inPPFX(NULL),
		m_outPPFX(NULL),
		m_lastFBO(NULL)
	{}

	void AddTask(RenderTask* task);

	void PreFramebufferBind(uint fboid);
	void PostFramebufferBind(uint fboid);

	void PreTaskRender(uint fboid, RenderTask* task);
	void PostTaskRender(uint fboid, RenderTask* task);

	void PostSceneRendering(uint fboid);

	void BindMaterial(uint fboid, RenderTask* task);
	void BindCamera();

	void PrePostprocess();
	void PostPostprocess();
};

/*--------------------------------------------------------------------------*/

class IRenderPhase;

class IRendererImpl :
	public IRenderer
{
public:
	IRendererImpl();
	virtual ~IRendererImpl();

	/*--IRenderer---------------------------------------------------------------*/
	void				InitializeUniformBuffers();
	GLUniformBuffer*	GetUniformBuffer(EUbo::Name name);

	void				InitializeTextureBuffers();
	GLTexture*			GetTextureBuffer(ETexture::Name name);

	IRenderPhase*		CreateRenderPhase(const std::string& name, int priority, ERenderPhaseMethod::Method method, SceneInstance* sceneinstance, ICamera* camera, GLTexture* output);
	void				DeleteRenderPhase(SceneInstance* sceneInstance);

	IRenderPhase*		GetUIRenderPhase();
	void				ProcessPhases();

	void				InitializeFont();

	/*--------------------------------------------------------------------------*/

	void				SetModelMatrix(const glm::mat4& modelmatrix);
	void				SetViewMatrix(const glm::mat4& viewMatrix);
	void				SetProjectionMatrix(const glm::mat4& projectionMatrix);
	void				UpdateModelMatrices();
	void				UpdateCameraMatrices();

	/*--------------------------------------------------------------------------*/

	void				ShowSplashscreen(ESplashscreen::Type type, const std::string& label, GLTexture* texture = NULL, Font* font = NULL, float progress = 0.0f);
	void				SaveScreenshot(const std::string& path);
	void				SwapBuffers();

	/*--------------------------------------------------------------------------*/

	void				UpdateUBOs();

	/*--------------------------------------------------------------------------*/

	void				DrawRectangle2D(const glm::vec2& p1, const glm::vec2& p2, const glm::vec2& p3, const glm::vec2& p4, GLTexture* texture, const Color& color = Color::White);
	void				DrawRectangle2D(const glm::vec2& p1, const glm::vec2& p2, GLTexture* texture, const Color& color = Color::White);
	void				DrawTriangle2D(const glm::vec2& p1, const glm::vec2& p2, const glm::vec2& p3, GLTexture* texture, const Color& color = Color::White);
	void				DrawLine2D(const glm::vec2& p1, const glm::vec2& p2, GLTexture* texture, const Color& color = Color::White);
	void				DrawLine3D(const glm::vec3& p1, const glm::vec3& p2, GLTexture* texture, const Color& color = Color::White);
	void				DrawText2D(const Font* font, const std::string& content, const glm::vec2& position, const Color& color, EFontStyle::Type style = EFontStyle::Normal);

	/*--------------------------------------------------------------------------*/

	void				BindPPFXA();
	void				BindPPFXB();

	/*--------------------------------------------------------------------------*/

private:
	GLUniformBuffer**			m_uniformBuffers;
	GLTexture**					m_textureBuffers;

	/*--------------------------------------------------------------------------*/
	std::vector<IRenderPhase*>	m_renderPhases;
	IRenderPhase*				m_uiRenderPhase;

	void						SortPhases();
	void						ProcessPhase(RenderPhase* phase);

	/*--------------------------------------------------------------------------*/

	glm::mat4					m_modelMatrix;
	glm::mat4					m_viewMatrix;
	glm::mat4					m_projectionMatrix;

	glm::mat4					m_viewProjectionMatrix;
	glm::mat4					m_modelViewMatrix;
	glm::mat4					m_modelViewProjectionMatrix;

	glm::mat4					m_invProjectionMatrix;
	glm::mat4					m_invViewProjectionMatrix;

	uint				m_matricesDirtyFlags;

	/*--------------------------------------------------------------------------*/

	const Material*				m_primitivesMat;

	/*--------------------------------------------------------------------------*/

	Font*						res_defaultFont;
	const Material*				m_fontMaterial;
	GLUniformBuffer*			ubo_font;
	GLVertexBuffer*				m_lettersInstancedData;
	GLVertexArray*				m_lettersInstancingVao;

	/*--------------------------------------------------------------------------*/
	GLFramebuffer*				m_ppfx_a_FBO;
	GLFramebuffer*				m_ppfx_b_FBO;

	/*--------------------------------------------------------------------------*/
};
